import { _decorator, Node, Button, Label, color } from 'cc';
import { BasePanel } from '../../../../scripts/libs/ui/BasePanel';
import { KeyDown } from '../../../../scripts/libs/rxjs/cc3/KeyEvent';
import { KeyCode } from 'cc';
import { lm } from '../../LobbyManager';
import { FlowStatus } from '../../Enums';
import { gm } from '../../../../scripts/managers/GameManager';
import { concat, map, Observable, of, Subscription } from 'rxjs';
import { BindButton } from '../../../../scripts/libs/rxjs/cc3/BindButton';
import { ResourceHelper } from '../../ResourceHelper';
import { MergeASAP } from '../../../../scripts/libs/rxjs/cc3/Merge';
import { find } from 'cc';
import { view } from 'cc';
import { udm } from '../../../../scripts/managers/UserDataManager';
import { DevConfig, FeatureKey, ItemKey, ItemParam } from '../../../../scripts/managers/DevConfig';
import { Animation } from 'cc';
import { linear } from '../../../../scripts/libs/utils/math';
import { UITransform } from 'cc';
import { FeatureUnlockPanel } from '../FeatureUnlockPanel/FeatureUnlockPanel';
import { Vec3 } from 'cc';
import { ParticleSystem } from 'cc';
import { Timer } from '../../../../scripts/libs/rxjs/cc3/CreatorSchedule';
import { nc } from '../../../../scripts/libs/rxjs/NotificationCenter';
import { UIOpacity } from 'cc';
import { Burst0 } from '../../../Particle/Burst0/Burst0';
import { v3 } from 'cc';
import { ParticleSystem2D } from 'cc';
import { Camera } from 'cc';
import { isValid } from 'cc';
import { Color } from 'cc';
import { v2 } from 'cc';
import { MSResolve, MSRToString_tm_s } from '../../../../scripts/libs/utils/date';
import { mat4 } from 'cc';
import { JumpTo_2D, TweenToStart, TweenToStartBSpine } from '../../../../scripts/libs/rxjs/cc3/TweenStart';
import { instantFunc } from '../../../../scripts/libs/rxjs/cc3/CommonObservable';
import { Burst1 } from '../../../Particle/Burst0/Burst1';
import { SettingsPanel } from '../SettingsPanel/SettingsPanel';
import { ReactiveProperty } from '../../../../scripts/libs/rxjs/cc3/ReactiveProperty';
import { StringTools } from '../../../../scripts/libs/utils/StringTools';
import { nullifySubscription } from '../../../../scripts/libs/rxjs/utils';
import { HardTipPanel } from '../HardTipPanel/HardTipPanel';
import { AnimationPlay } from '../../../../scripts/libs/rxjs/cc3/AnimationPlay';
import { Sprite } from 'cc';
import { Colors } from '../../game/basic';
import { Burst2 } from '../../../Particle/Burst0/Burst2';
import { notification_sdk_m } from '../../../../scripts/managers/SDK/NotificationCocosSDKManager';
import { misc_sdk_m } from '../../../../scripts/managers/SDK/MiscSDKManager';
import { Quat } from 'cc';
import { ReactiveSet } from '../../../../scripts/libs/rxjs/cc3/ReactiveSet';
import { KeyFly } from '../../../Particle/key0/KeyFly';
import { Burst3 } from '../../../Particle/Burst0/Burst3';
import { Burst4 } from '../../../Particle/Burst0/Burst4';
import { am } from '../../../../scripts/managers/AudioManager';
import { Prefab } from 'cc';
import { instantiate } from 'cc';
import { Stage3dView } from '../../stage3d/Stage3dView';
const { ccclass, property } = _decorator;

const _v30 = v3();
const _m0 = mat4();

@ccclass('GamePanel')
export class GamePanel extends BasePanel {

    stage3dContainer: Node = null;
    stage3d: Stage3dView = null;

    @property(Prefab)
    stage3dPrefab: Prefab = null;

    @property(Label)
    stageNumLabel: Label = null;

    @property(Button)
    gamePauseButton: Button = null;

    @property(Button)
    gameRestartButton: Button = null;

    @property(Node)
    fxContainer: Node = null;

    @property(Node)
    topNode: Node = null;

    @property(Label)
    remainLabel: Label = null;

    @property(Label)
    coinCountLabel: Label = null;

    coinValue = ReactiveProperty.Create(0);

    @property(Label)
    tipLabel: Label = null;

    @property(Sprite)
    backgroundSprite: Sprite = null;

    @property(Sprite)
    backgroundShineSprite: Sprite = null;

    @property(Sprite)
    remainBoardSprite: Sprite = null;

    @property(Sprite)
    skullSprite: Sprite = null;

    willBind(): void {
        this.stage3dContainer = lm.lobbySceneCanvas.node3dContainer;
        let stage3dNode = instantiate(this.stage3dPrefab);
        this.stage3d = stage3dNode.getComponent(Stage3dView);
        this.stage3d.gamePanel = this;
        this.stage3dContainer.addChild(stage3dNode);

        this.adaptive();
        lm.lobbySceneCanvas.removeBgReasons.add('in_game');
    }

    readyPopupPanel = ReactiveProperty.Create(true);

    useObserves() {
        return [
            gm.fullScreenChangedCommand.subscribe(_ => {
                this.adaptive();
            }),
            BindButton(this.gamePauseButton).subscribe(this.pauseButtonClicked.bind(this)),
            BindButton(this.gameRestartButton).subscribe(this.gameRestartButtonClicked.bind(this)),
            // BindButton(this.swapperButton).subscribe(this.swapperButtonClicked.bind(this)),
            // BindButton(this.magnetButton).subscribe(this.magnetButtonClicked.bind(this)),
            // BindButton(this.balloonButton).subscribe(this.balloonButtonClicked.bind(this)),
            KeyDown().subscribe((_: KeyCode) => {
                if (_ == KeyCode.ESCAPE) {
                    lm.flowStatus.value = FlowStatus.EditorGame; // 返回编辑模式
                }
            }),
            gm.loadGameCommand.subscribe(this.loadStage.bind(this)),
            gm.loadNextGameCommand.subscribe(this.loadNextStage.bind(this)),

            // MergeASAP(udm.itemSwapper, udm.itemMagnet, udm.itemBalloon, gm.itemAnimatingFlags).subscribe(this.refreshItems.bind(this)),
            nc.subscribe('change_smoke', this.genChangeSmoke.bind(this)),

            gm.gameHudHide.subscribe(_ => {
                if (gm.gameHudHide.value) {
                    this.topNode.getComponent(UIOpacity).opacity = 0;
                    // this.bottomNode.getComponent(UIOpacity).opacity = 0;
                } else {
                    this.topNode.getComponent(UIOpacity).opacity = 255;
                    // this.bottomNode.getComponent(UIOpacity).opacity = 255;
                }
            }),

            MergeASAP(gm.curTime, gm.addTime).subscribe(this.refreshRemain.bind(this)),


            this.coinValue.subscribe(this.refreshCoin.bind(this)),
            MergeASAP(udm.coin, gm.coinAnimatingFlags, udm.temp_coin_diff).subscribe(this.refreshCoinTarget.bind(this)),

            gm.gameHudHide.subscribe(_ => this.topNode.getComponent(UIOpacity).opacity = _ ? 0 : 255),

            // this.waitPopupReason.subscribe(_ => {
            //     if (this.waitPopupReason)
            // })
            gm.pauseReason.subscribe(_ => {
                if (gm.pauseReason.size > 0) {
                    this.readyPopupPanel.value = false;
                } else {
                    this.readyPopupPanel.value = true;
                }
            }),

            gm.firstTouched.subscribe(_ => {
                if (gm.firstTouched.value) {
                    this.gameRestartButton.getComponentsInChildren(Sprite).forEach(_ => _.color = color().fromHEX('ffffff'));
                    this.gameRestartButton.interactable = true;
                } else {
                    this.gameRestartButton.getComponentsInChildren(Sprite).forEach(_ => _.color = color().fromHEX('8F8F8F'));
                    this.gameRestartButton.interactable = false;
                }
            }),
        ];
    }

    onDestroyView(): void {
        this._sub_smoke_list.forEach(sub => sub.unsubscribe());
        nullifySubscription.call(this, '_sub_coin_value_tween');
        lm.lobbySceneCanvas.removeBgReasons.delete('in_game');
        this.stage3d = null;
        this.stage3dContainer.destroyAllChildren();
    }

    adaptive() {
        // let height = view.getVisibleSize().height;
        // let scale = linear(1650, 2400, .7, 1, height);
        // // this.stageView.node.setScale(scale, scale, scale);
        // this.buttonsNode.setScale(scale, scale, scale);

        // let bottomHeight = linear(1650, 2400, 300, 500, height);
        // this.bottomNode.getComponent(UITransform).height = bottomHeight;
    }

    start() {
        this.loadStage();
    }

    _initStage = false;

    loadStage() {
        this._initStage = true;

        this.node.clearSubscriptions();

        if (this.fxContainer != null) this.fxContainer.destroyAllChildren();

        this.stage3d.loadStage(gm.stageInfo.value);
        // this.stageBoard.stageInfo.value = gm.stageInfo.value;

        udm.gameStart(gm.stageInfo.value);

        let hardColorKey = 'blue';
        if (gm.stageInfo.value.hard == 1) hardColorKey = 'purple';
        if (gm.stageInfo.value.hard == 2) hardColorKey = 'red';
        this.remainBoardSprite.spriteFrame = this.remainBoardSprite.spriteAtlas.getSpriteFrame(`stage/remain_board_${hardColorKey}`);
        this.backgroundSprite.spriteFrame = this.backgroundSprite.spriteAtlas.getSpriteFrame(`stage/background_${hardColorKey}`);
        if (gm.stageInfo.value.hard >= 1) {
            this.backgroundShineSprite.spriteFrame = this.backgroundShineSprite.spriteAtlas.getSpriteFrame(`stage/background_shine_${hardColorKey}`);
            this.backgroundShineSprite.node.active = true;
        } else {
            this.backgroundShineSprite.node.active = false;
        }

        this.refreshStageId();
        // this.refreshItemEnabled();

        if (gm.stageInfo.value.tip == 'tutorial0') {
            this.tipLabel.node.active = true;
            this.tipLabel.string = 'Drag To Blast';
        } else {
            this.tipLabel.node.active = false;
            this.tipLabel.string = '';
        }

        this.readyPopupPanel.subscribeWhenTrue(() => {
            concat(
                this.tryOpenHardTipPanel(),
                this.tryOpenFeatureUnlockPanel(),
            ).subscribe().addTo(this.node);
        }).addTo(this.node);


        if (gm.stageInfo.value.stageId >= DevConfig.REQUEST_NOTIFICATION_GREATEREQUAL_STAGE_NUM) {
            notification_sdk_m.requestPermission();
        }

        this._initStage = false;
    }

    tryOpenFeatureUnlockPanel() {
        if (['arrow_block',
            'combined_block',
            'ice_block',
            'multi_blast',
            'layer_block',
            'chain_block',
            'shifting_block',
            'colorful_path',
            'claw_block',
            'ice_gate',
            'hidden_block'].includes(gm.stageInfo.value.tip)) {
            return FeatureUnlockPanel.StartProcess((panel: FeatureUnlockPanel) => {
                // this.log('@tryOpenFeatureUnlockPanel', key);
                panel.featureKey.value = gm.stageInfo.value.tip as FeatureKey;
            });
        }
        return of(null);
    }

    tryOpenHardTipPanel() {
        let hard = gm.stageInfo.value?.hard ?? 0;
        if (hard > 0) {
            return HardTipPanel.StartProcess((panel: HardTipPanel) => {
                panel.hard.value = hard;
            })
        }
        return of(null);
    }


    retryButtonClicked() {
        udm.gameLose(this.stage3d.stageInfo, 'restart');
        this.loadStage();
    }

    pauseButtonClicked() {
        SettingsPanel.StartProcess().subscribe();
    }

    gameRestartButtonClicked() {
        this.retryButtonClicked();
    }

    editButtonClicked() {
        lm.flowStatus.value = FlowStatus.EditorGame; // 返回编辑模式
    }

    loadNextStage() {
        lm.customLoadingReasons.add('load_stage');
        let targetStageId = gm.stageInfo.value.stageId + 1;
        lm.lobbySceneCanvas.loadItem().subscribe(_ => {
            ResourceHelper.LoadStageAssets(targetStageId).subscribe(stageInfo => {
                if (stageInfo != null) {
                    lm.customLoadingReasons.delete('load_stage');
                    if (stageInfo.stageId != null && stageInfo.stageId != targetStageId) {
                        this.warn('StageId in json is not match with stageId in file name.');
                        return;
                    }
                    stageInfo.stageId = targetStageId;
                    gm.stageInfo.value = stageInfo;
                    this.loadStage();
                    lm.lobbySceneCanvas.loadItem(true).subscribe();
                } else {
                    lm.customLoadingReasons.delete('load_stage');
                }
            });
        });
    }

    refreshStageId() {
        if (this.stage3d.stageInfo != null) {
            this.stageNumLabel.string = `LEVEL ${this.stage3d.stageInfo.stageId}`;

            if (this.stage3d.stageInfo.hard > 0) {
                this.skullSprite.node.active = true;
                let hardColorKey = 'blue';
                if (this.stage3d.stageInfo.hard == 1) {
                    hardColorKey = 'purple';
                    this.stageNumLabel.color = color().fromHEX('#fe71f6');
                } else if (this.stage3d.stageInfo.hard = 2) {
                    hardColorKey = 'red';
                    this.stageNumLabel.color = color().fromHEX('#ff7070');
                }
                this.skullSprite.spriteFrame = this.skullSprite.spriteAtlas.getSpriteFrame(`stage/skull_${hardColorKey}`);

            } else {
                this.stageNumLabel.color = Color.WHITE;
                this.skullSprite.node.active = false;
            }

        } else {
            this.stageNumLabel.string = 'n/a';
        }
    }

    _sub_smoke_list: Set<Subscription> = new Set();
    genChangeSmoke(param: { worldPos: Vec3 }) {
        let smokeNode = ResourceHelper.ChangeSmokePool.getNode();
        smokeNode.setParent(this.fxContainer);
        smokeNode.setWorldPosition(param.worldPos);
        let ps = smokeNode.getComponent(ParticleSystem);
        ps.play();
        let sub = Timer(1, smokeNode).subscribe(_ => {
            ps.stop();
            ResourceHelper.ChangeSmokePool.putNode(smokeNode);
            this._sub_smoke_list.delete(sub);
        });
        this._sub_smoke_list.add(sub);
    }

    burst0(worldPos: Vec3, colorId: number) {
        let node = ResourceHelper.Burst0Pool.getNode();
        let burst = node.getComponent(Burst0);
        burst.colorId = colorId;
        node.setParent(this.fxContainer);
        node.setWorldPosition(worldPos);
        burst.burst();
    }

    burst1(worldPos: Vec3) {
        let node = ResourceHelper.Burst1Pool.getNode();
        let burst = node.getComponent(Burst1);
        node.setParent(this.fxContainer);
        node.setWorldPosition(worldPos);
        burst.burst();
    }

    burst2(worldPos: Vec3) {
        let node = ResourceHelper.Burst2Pool.getNode();
        let burst = node.getComponent(Burst2);
        node.setParent(this.fxContainer);
        node.setWorldPosition(worldPos);
        burst.burst();
    }

    burst3(worldPos: Vec3) {
        let node = ResourceHelper.Burst3Pool.getNode();
        node.setParent(this.fxContainer);
        node.setWorldPosition(worldPos);
        let burst3 = node.getComponent(Burst3);
        burst3.burst();
    }

    burst4(worldPos: Vec3) {
        let node = ResourceHelper.Burst4Pool.getNode();
        node.setParent(this.fxContainer);
        node.setWorldPosition(worldPos);
        let burst4 = node.getComponent(Burst4);
        burst4.reset();
        burst4.burst();
    }

    // blink0(worldPos: Vec3, camera?: Camera) {

    //     if (camera != null) {
    //         camera.convertToUINode(worldPos, this.fxContainer, _v30);
    //     } else {
    //         this.fxContainer.inverseTransformPoint(_v30, worldPos);
    //     }


    //     let node = ResourceHelper.BlinkPlateDestroyPool.getNode();
    //     node.setParent(this.fxContainer);
    //     node.setPosition(_v30);
    //     // node.setWorldPosition(worldPos);
    //     node.getComponent(Animation).play();
    //     node.getComponentInChildren(ParticleSystem2D).resetSystem();
    //     Timer(.4, node).subscribe(_ => {
    //         if (isValid(this?.node)) {
    //             node.getComponentInChildren(ParticleSystem2D).stopSystem();
    //             ResourceHelper.BlinkPlateDestroyPool.putNode(node);
    //         }
    //     });
    // }

    // genBlockRemoveParticle(worldPos: Vec3, c: Color, angle: number) {
    //     let node = ResourceHelper.BlockRemoveParticlePool.getNode();
    //     node.setParent(this.fxContainer);
    //     node.setWorldPosition(worldPos);
    //     node.setRotationFromEuler(0, 0, angle);
    //     node.setScale(v3(Vec3.ONE).multiplyScalar(this.stage3d.scaleSize));
    //     let ps2ds = node.getComponentsInChildren(ParticleSystem2D);
    //     for (let ps2d of ps2ds) {
    //         let pv = linear(0, 1, 0, 50, this.stage3d.scaleSize);
    //         ps2d.posVar = v2(pv, pv);
    //         ps2d.startColor = color(c.r, c.g, c.b, 255);
    //         ps2d.endColor = color(c.r, c.g, c.b, 0);
    //         ps2d.resetSystem();
    //     }
    //     Timer(.6, node).subscribe(_ => {
    //         if (isValid(this?.node)) {
    //             node.getComponentInChildren(ParticleSystem2D).stopSystem();
    //             ResourceHelper.BlockRemoveParticlePool.putNode(node);
    //         }
    //     });
    // }

    genStarBurst1(worldPos: Vec3, color: Color) {
        let starBurstNode = ResourceHelper.StarBurst1Pool.getNode();
        nullifySubscription.call(starBurstNode, '_sub');
        starBurstNode.setParent(this.fxContainer);
        starBurstNode.setWorldPosition(worldPos);
        let circleNode = starBurstNode.getChildByName('circle');
        circleNode.getComponent(Sprite).color = color;
        let anim = circleNode.getComponent(Animation)
        AnimationPlay(anim, 'circle').subscribe();
        let p2d = starBurstNode.getComponentInChildren(ParticleSystem2D);
        p2d.startColor = color;
        p2d.resetSystem();
        let _sub = concat(
            Timer(.1, starBurstNode),
            instantFunc(() => {
                p2d.stopSystem();
            }),
            Timer(.4, starBurstNode),
        ).subscribe({
            complete: () => {
                ResourceHelper.StarBurst1Pool.putNode(starBurstNode);
            }
        });
        starBurstNode.attr({ _sub });
    }

    recycleAllStartBurst() {
        this.fxContainer.children.map(_ => _).forEach(_ => {
            nullifySubscription.call(_, '_sub');
            ResourceHelper.StarBurst1Pool.putNode(_);
        });
    }

    refreshRemain() {
        let totalSecond = this.stage3d?.stageInfo?.time;

        if (totalSecond != null) {
            totalSecond += gm.addTime.value;
            let curSecond = gm.curTime.value;
            let remain = totalSecond - curSecond;
            if (remain < 0) remain = 0;
            let msr = MSResolve(remain * 1000);
            this.remainLabel.string = MSRToString_tm_s(msr);
        } else {
            this.remainLabel.string = '∞';
        }
    }

    iceAtt(fromWorldPos, toWorldPos, callback: () => void) {
        let node = ResourceHelper.IceAttPool.getNode();
        node.clearSubscriptions();
        node.attr({ _lastPos: null });
        node.setParent(this.fxContainer);
        node.setWorldPosition(fromWorldPos);
        find('tail', node).active = true;
        let ps2d = node.getComponentsInChildren(ParticleSystem2D);
        ps2d.forEach(_ => {
            _.resetSystem();
        });
        let mainNode = node.getChildByName('main');
        mainNode.active = true;
        let angle = Math.atan2(toWorldPos.y - fromWorldPos.y, toWorldPos.x - fromWorldPos.x) * 180 / Math.PI + 90;
        mainNode.setRotationFromEuler(0, 0, angle);
        let duration = Vec3.distance(fromWorldPos, toWorldPos) / 3000;


        // 曲线，中间添加一点
        let distance = Vec3.distance(fromWorldPos, toWorldPos);

        let centerPos = v3(fromWorldPos).lerp(toWorldPos, 0.5);
        const rotationQuat = new Quat();
        Quat.fromAxisAngle(rotationQuat, Vec3.UNIT_Z, 90 * Math.PI / 180);
        let directionVec = v3(toWorldPos).subtract(fromWorldPos);
        let result = v3();
        Vec3.transformQuat(result, directionVec, rotationQuat);
        result.normalize();
        result.multiplyScalar(distance / 5);
        result.multiplyScalar(Math.random() * 2 - 1);
        centerPos.add(result);


        let pList: Vec3[] = [
            this.fxContainer.inverseTransformPoint(v3(), fromWorldPos),
            this.fxContainer.inverseTransformPoint(v3(), centerPos),
            this.fxContainer.inverseTransformPoint(v3(), toWorldPos),
        ];

        concat(
            TweenToStartBSpine(node, duration, pList, 3, { easing: 'quadIn' }),
            instantFunc(() => {
                ps2d.forEach(_ => _.stopSystem());
                mainNode.active = false;
                node.attr({ _lastPos: null });
                this.burst0(toWorldPos, null);
                this.burst1(toWorldPos);
                if (callback) callback();
                misc_sdk_m.requestHapic('light');
            }),
            Timer(.5),
            instantFunc(() => {
                find('tail', node).active = false;
            })
        ).subscribe({
            complete: () => {
                if (isValid(this?.node)) {
                    ResourceHelper.IceAttPool.putNode(node);
                }
            }
        }).addTo(node);
    }



    iceAttArrow(fromWorldPos, toWorldPos, callback: () => void) {
        let node = ResourceHelper.IceAttPool.getNode();
        node.clearSubscriptions();
        node.attr({ _lastPos: null });
        node.setParent(this.fxContainer);
        node.setWorldPosition(fromWorldPos);
        find('tail', node).active = true;
        let ps2d = node.getComponentsInChildren(ParticleSystem2D);
        ps2d.forEach(_ => {
            _.resetSystem();
        });
        let mainNode = node.getChildByName('main');
        let angle = Math.atan2(toWorldPos.y - fromWorldPos.y, toWorldPos.x - fromWorldPos.x) * 180 / Math.PI + 90;
        mainNode.setRotationFromEuler(0, 0, angle);
        let duration = Vec3.distance(fromWorldPos, toWorldPos) / 3000;


        // 曲线，中间添加一点
        let distance = Vec3.distance(fromWorldPos, toWorldPos);

        let centerPos = v3(fromWorldPos).lerp(toWorldPos, 0.5);
        const rotationQuat = new Quat();
        Quat.fromAxisAngle(rotationQuat, Vec3.UNIT_Z, 90 * Math.PI / 180);
        let directionVec = v3(toWorldPos).subtract(fromWorldPos);
        let result = v3();
        Vec3.transformQuat(result, directionVec, rotationQuat);
        result.normalize();
        result.multiplyScalar(distance / 5);
        result.multiplyScalar(Math.random() * 2 - 1);
        centerPos.add(result);


        let pList: Vec3[] = [
            this.fxContainer.inverseTransformPoint(v3(), fromWorldPos),
            this.fxContainer.inverseTransformPoint(v3(), centerPos),
            this.fxContainer.inverseTransformPoint(v3(), toWorldPos),
        ];

        concat(
            TweenToStartBSpine(node, duration, pList, 3, { easing: 'quadIn' }),
            instantFunc(() => {
                ps2d.forEach(_ => _.stopSystem());
                mainNode.active = false
                node.attr({ _lastPos: null });
                // this.burst0(toWorldPos, null);
                // this.burst1(toWorldPos);
                if (callback) callback();
                misc_sdk_m.requestHapic('light');
            }),
            Timer(.5),
            instantFunc(() => {
                find('tail', node).active = false;
            })
        ).subscribe({
            complete: () => {
                if (isValid(this?.node)) {
                    ResourceHelper.IceAttPool.putNode(node);
                }
            }
        }).addTo(node);
    }

    blindAtt(fromWorldPos: Vec3, toWorldPos: Vec3, delay: number, callback: () => void) {

        let node = ResourceHelper.BlindAttPool.getNode();
        node.clearSubscriptions();
        node.attr({ _lastPos: null });

        find('tail', node).active = true;
        let ps2d = node.getComponentsInChildren(ParticleSystem2D);
        ps2d.forEach(_ => {
            _.resetSystem();
        });
        let mainNode = node.getChildByName('main');
        let angle = Math.atan2(toWorldPos.y - fromWorldPos.y, toWorldPos.x - fromWorldPos.x) * 180 / Math.PI + 90;
        mainNode.setRotationFromEuler(0, 0, angle);
        let duration = Vec3.distance(fromWorldPos, toWorldPos) / 3000;


        // 曲线，中间添加一点
        let distance = Vec3.distance(fromWorldPos, toWorldPos);

        let centerPos = v3(fromWorldPos).lerp(toWorldPos, 0.5);
        const rotationQuat = new Quat();
        Quat.fromAxisAngle(rotationQuat, Vec3.UNIT_Z, 90 * Math.PI / 180);
        let directionVec = v3(toWorldPos).subtract(fromWorldPos);
        let result = v3();
        Vec3.transformQuat(result, directionVec, rotationQuat);
        result.normalize();
        result.multiplyScalar(distance / 5);
        result.multiplyScalar(Math.random() * 2 - 1);
        centerPos.add(result);


        let pList: Vec3[] = [
            this.fxContainer.inverseTransformPoint(v3(), fromWorldPos),
            this.fxContainer.inverseTransformPoint(v3(), centerPos),
            this.fxContainer.inverseTransformPoint(v3(), toWorldPos),
        ];

        concat(
            Timer(delay, node),
            instantFunc(() => {
                node.setParent(this.fxContainer);
                node.setWorldPosition(fromWorldPos);
            }),
            TweenToStartBSpine(node, duration, pList, 3, { easing: 'quadIn' }),
            instantFunc(() => {
                ps2d.forEach(_ => _.stopSystem());
                mainNode.active = false
                node.attr({ _lastPos: null });
                // this.burst0(toWorldPos, null);
                // this.burst1(toWorldPos);
                if (callback) callback();
                misc_sdk_m.requestHapic('light');
            }),
            Timer(.5),
            instantFunc(() => {
                find('tail', node).active = false;
            })
        ).subscribe({
            complete: () => {
                if (isValid(this?.node)) {
                    ResourceHelper.BlindAttPool.putNode(node);
                }
            }
        }).addTo(node);
    }

    keyFly(fromWorldPos, toWorldPos, callback: () => void, needChainBurst) {
        let node = ResourceHelper.KeyFlyPool.getNode();
        let keyFly = node.getComponent(KeyFly);
        keyFly.reset();
        // node.attr({ _lastPos: null });
        node.setParent(this.fxContainer);
        node.setWorldPosition(fromWorldPos);
        node.setSiblingIndex(-1);
        find('tail', node).active = true;
        let ps2d = node.getComponentsInChildren(ParticleSystem2D);
        ps2d.forEach(_ => {
            _.resetSystem();
        });
        // let mainNode = node.getChildByName('main');
        // mainNode.active = true;
        let duration = Vec3.distance(fromWorldPos, toWorldPos) / 700;

        let toLocalPos = v3();
        this.fxContainer.inverseTransformPoint(toLocalPos, toWorldPos);

        keyFly.go();

        concat(
            JumpTo_2D(node, duration, toLocalPos, 200, { easing: 'quadIn' }),

            instantFunc(() => {
                ps2d.forEach(_ => _.stopSystem());

                // mainNode.active = false;
                keyFly.openKey(() => {
                    // this.burst1(toWorldPos);
                    // !needChainBurst && this.burst0(toWorldPos, null);
                    needChainBurst && this.burst2(toWorldPos);
                    this.burst3(toWorldPos);

                    if (needChainBurst) {
                        am.playSfx({ clipName: `ab_lobby/Audio/sfx/keyopenlockerlast` });
                    } else {
                        am.playSfx({ clipName: `ab_lobby/Audio/sfx/keyopenlocker` });
                    }

                    misc_sdk_m.requestHapic('light');
                    if (callback) callback();
                });
                // node.attr({ _lastPos: null });

            }),
            Timer(.5),
            instantFunc(() => {
                find('tail', node).active = false;
            })
        ).subscribe({
            // complete: () => {
            //     if (isValid(this?.node)) {
            //         ResourceHelper.KeyFlyPool.putNode(node);
            //     }
            // }
        }).addTo(node);
    }


    refreshCoin() {
        this.coinCountLabel.string = StringTools.toKDotString(Math.floor(this.coinValue.value));
    }

    _sub_coin_value_tween: Subscription = null;
    refreshCoinTarget() {
        if (udm.temp_coin_diff.value != null) {
            this._sub_coin_value_tween = TweenToStart(this.coinValue, .4, { value: this.coinValue.value + udm.temp_coin_diff.value }).subscribe();
            return;
        }
        if (gm.coinAnimatingFlags.size > 0) return;
        nullifySubscription.call(this, '_sub_coin_value_tween');
        this._sub_coin_value_tween = TweenToStart(this.coinValue, .4, { value: udm.coin.value }).subscribe();
    }

    iceBurst0(worldPos: Vec3) {
        let node = ResourceHelper.IceBurst0Pool.getNode();
        node.clearSubscriptions();
        node.setParent(this.fxContainer);
        node.setWorldPosition(worldPos);
        let ps = node.getComponent(ParticleSystem);
        ps.play();
        concat(
            Timer(.1, node),
            instantFunc(() => {
                ps.stopEmitting();
            }),
            Timer(1, node),
        ).subscribe({
            complete: () => {
                if (isValid(this?.node)) {
                    ps.stop();
                    node.clearSubscriptions();
                    ResourceHelper.IceBurst0Pool.putNode(node);
                }
            }
        }).addTo(node);
    }

    iceBurst1(worldPos: Vec3) {
        let node = ResourceHelper.IceBurst1Pool.getNode();
        node.clearSubscriptions();
        node.setParent(this.fxContainer);
        node.setWorldPosition(worldPos);
        let ps = node.getComponent(ParticleSystem);
        ps.play();
        concat(
            Timer(.1, node),
            instantFunc(() => {
                ps.stopEmitting();
            }),
            Timer(1, node),
        ).subscribe({
            complete: () => {
                if (isValid(this?.node)) {
                    ps.stop();
                    node.clearSubscriptions();
                    ResourceHelper.IceBurst1Pool.putNode(node);
                }
            }
        }).addTo(node);
    }
}

